package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)
/*考驾照{
考场签到，名字丢入管道;
只有5个车道，最多供5个人同时考试;
考生按签到顺序依次考试，给予考生10%的违规几率;
每3秒钟巡视一次，发现违规的清出考场，否则输出考场时序良好;
所有考试者考完后，生成考试记录:
当前目录下的成绩录入MySQL数据库,数据库允许一写多读;
成绩录入完毕通知考生，考生查阅自己的成绩;*/

var ( //函数外同意定义全局变量
	chnames = make(chan string,100) //定义一个100人的大管道
	chlanes = make(chan int,5) //并发管道为5

	//声明一个等待组
	wg sync.WaitGroup
	//WaitGroup对象内部有个计时器， 最初从0 开始， 他有3个方法 Add() , Done(), Wait()用来控制计数器的数量
	// Add(n) 把计数器设置成n, Done() 每次把计数器-1， wait() 会阻塞代码的运行， 直到计数器的值减为0

	randommutex sync.Mutex//随机数互斥锁，确保getrandomint不能被并发访问；sync.Mutex表示互斥锁

	//姓氏
	familynames = []string{"赵","钱","孙","李","周","吴","郑","王","冯","陈","褚","卫","蒋","沈","韩","杨","秦","许","何","吕","施","张","孔","曹","严","华","金","魏","陶","姜","戚","谢","邹","柏","窦","章","包","邵"}
	//辈分
	middlenames = map[string][]string{}
	//最后一个字
	lastnames = []string{"春","夏","秋","冬","风","霜","雨","雪","福","禄","寿","喜","文","武","才","华","东","南","西","北","金","木","水","火","土","山","石","竹","田","海","阔","天","空","光","辉","岁","月"}
)

func init()  { //  这里init函数用于进行各个姓氏的班辈初始化
	// init()函数会在每个包（main）完成初始化后自动执行，并且执行优先级比main函数高。init 函数通常被用来：
	//对变量进行初始化  检查修复程序的状态  注册  运行一次计算
	for _,names:=range familynames{
		middlenames[names] = []string{"允","文","遵","祖","训","钦","武","大","君","胜","顺","道","宜","逢","吉","师","良","善","用","晟","尚","志","公","诚","秉","惟","怀","敬","谊","存","辅","嗣","资","廉","直","匡","时","永","信","敦","孟","季","均","荣","显","英","华","蕴","盛","容","宏","才","升","博","衍"}
	}
}

func main()  {
	for i:=0;i<100;i++{
		chnames<-getrandomname() //每个管道赋一个值
		//  <-是对chan类型来说的。chan类型类似于一个数组。 <- chan表示对chan中的数据读取；相反 chan <- value 是对chan赋值。
	}
	close(chnames) //close函数会往chan中压入一条特殊的通知消息，用来通知chan接收者不会再收到数据。所以即使chan中有数据也可以close()
	for name:=range chnames{
		//fmt.Println(name)
		//100个人考试
		wg.Add(1)
		go TakeExam(name)//只能5个人同时考试，需要开辟一条协程
		// go func表示这个函数会是以协程的方式运行。这样就可以提供程序的并发处理能力
		//协程,是同步执行,不是并行,只是切了一个上下文了,为你保存原来的上下文而已。切到第二个协程时,原来的协程处于挂起状态
	}
	wg.Wait()//主函数中使用Wait()一直阻塞，直到wg为零——也就是所有的100个for循环都运行完毕。
	fmt.Print("考试完毕")
}

func TakeExam(name string) {
	chlanes <-123  //往管道里头写东西
	fmt.Println(name,"正在考试......")
	<-time.After(time.Second) //1秒钟考完

	//生成考生成绩
	score :=getrandomint(0,100)
	//fmt.Println(score)
	//让getrandomint不并发访问（只同步访问），才能使得生成的分数不一样，因此要加一把互斥锁
	//不然每次都是五个一起访问，生成的分数就一模一样

	if score<10{
		score=0
		fmt.Println(name,"考试违纪!!!")
	}
	<-chlanes  //从管道里头读取东西
	wg.Done() //考完了注销，不然会造成协程死锁
}

func getrandomname()(name string)  {
	//生成随机名字
	familyname := familynames[getrandomint(0,len(familynames)-1)]
	middlename := middlenames[familyname][getrandomint(0,len(middlenames[familyname])-1)]
	lastname := lastnames[getrandomint(0,len(lastnames)-1)]
	return familyname+middlename+lastname
}

func getrandomint(start,end int) int {
	randommutex.Lock()
	<-time.After(time.Nanosecond) //相当于是睡了一个纳秒再进行下一步
	r:=rand.New(rand.NewSource(time.Now().UnixNano())) //设置随机数种子，可保证每次随机都是随机的
	n:=start+r.Intn(end-start+1) //rand.Intn 表示返回一个0到100之间(不包含100)的伪随机数, 返回的数据类型是int
	//fmt.Println(n)// n的值为  0+ [0,99]
	randommutex.Unlock()
	return n
}